/**
 * @version		$Id: manager.js 61 2011-02-19 20:41:32Z happy_noodle_boy $
 * @package      JCE
 * @copyright    Copyright (C) 2005 - 2009 Ryan Demmer. All rights reserved.
 * @author		Ryan Demmer
 * @license      GNU/GPL
 * JCE is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 */

/**
 Class: Manager
 Base manager class for creating a JCE Manager object.

 Arguments:
 options - optional, an object containing options.

 Options:
 interface 		- various interface identifiers.
 filter 			- file extension filter list
 tree 			- use folder tree (requires tree.js)
 onDeleteFiles 	- Delete files callback function.
 onDeleteFolder 	- Delete folder callback function.
 onRename 		- Fodler / file rename callback function.
 onNewFolder 	- New folder callback function.
 onListComplete 	- File / folder list load complete callback function.
 onFileClick 	- File click callback function.
 onFileDetails	- File details callback function.

 Example:
 var imgmanager = new Manager('imgmanager', src, args, {params: {'key': 'value'}});
 */
(function($) {

    $.widget("ui.MediaManager", {
        _actions 		: [],
        _buttons 		: {
            'folder'	: {},
            'file'		: {}
        },
        _dialog			: [],
        // Returned items array
        _returnedItems 	: [],
        // 'Clipboard'
        _pastefiles 	: '',
        _pasteaction	: '',
        // List limits
        _limitcount		: 0,
        _limitend 		: 0,

        options : {
            // base dir
        	dir : 'images',
        	// Various dialog containers
            dialog :{
                list:  		'#browser-list',
                tree: 		'#tree-body',
                info: 		'#browser-details-text',
                limit:		'#browser-list-limit',
                comments:	'#browser-details-comment',
                nav: 		'#browser-details-nav',
                status: 	'#browser-message',
                message:	'#message-info',
                buttons: 	'#browser-buttons',
                actions: 	'#browser-actions',
                refresh: 	'#refresh',
                search: 	'#search',
                sortExt:	'#sort-ext',
                sortName: 	'#sort-name'
            },
            vars:			null,
            actions:		null,
            buttons:		null,
            tree:			true,
            details:		true,
            search:			true,
            upload:			{
                size		: '1024kb',
                types		: {},
                overwrite	: true,
                limit		: false,
                onLoad		: function() {
                },

                onSelect 	: function() {
                },

                runtimes	: 'html5,flash,html4',
                chunk_size	: '1mb',
                insert		: true
            },
            listlimit	: 'all'
        },

        _init : function() {
            var self = this;

            // Create Actions and Button
            this._addActions(this.options.actions);
            this._addButtons(this.options.buttons);

            var dialog = this.options.dialog;

            // Build file and folder lists
            var list = document.createElement('ul');

            $(list).addClass('item-list').attr({'id' : 'item-list', 'role' : 'listbox', 'tabindex' : '-1'}).bind('click.item-list', function(e) {
                var n = e.target, p = n.parentNode;

                switch (n.nodeName) {
                    case 'A' :
                    	if ($(p).hasClass('folder')) {
                    		var id = p.id || self._getPreviousDir();
                            return self._changeDir(id);
                    	} else {
                    		self._setSelectedItems(e, true);
                            self._trigger('onFileClick', e, p);
                    	}
                        
                        break;
                    case 'LI' :
                        if ($(n).hasClass('folder-up')) {
                            var id = n.id || self._getPreviousDir();
                            return self._changeDir(id);
                        }

                        self._setSelectedItems(e, true);
                        break;
                    case 'SPAN' :
                        if ($(n).hasClass('checked')) {
                            $(n).removeClass('checked').attr('aria-checked', false);
                            self._removeSelectedItems([p], true);
                        } else {
                            self._setSelectedItems(e, true);
                            $(n).attr('aria-checked', true);
                        }
                        break;
                }
                
                e.preventDefault();
            }).bind('dblclick.item-list', function(e) {
                e.preventDefault();
                return false;
            }).bind('keydown.item-list', function(e) {
            	switch (e.which) {
	            	case 13:
	            		// get currently selected item
	            		n = $('li.selected:last', '#item-list').get(0);
	            		
	            		if (!n)
	            			return;
	            		
	            		if ($(n).hasClass('folder')) {
	            			var id = n.id || self._getPreviousDir();
                            return self._changeDir(id);
                    	} else {
                    		self._setSelectedItems(e, true);
                            self._trigger('onFileClick', e, n);
                    	}
	            		
	            		break;
	            	case 38:
            		case 40:
            			self._setSelectedItems(e);
            			break;
            	}

            	e.preventDefault();
            });

            $(dialog.list).append(list);

            // Info navigation buttons

            // Nav Left
            $(dialog.nav + '-left').click( function() {
                $('li.selected.active', '#item-list').removeClass('active').prev('li.selected').addClass('active');
                self._showItemDetails();
            });

            // Nav Right
            $(dialog.nav + '-right').click( function() {
            	$('li.selected.active', '#item-list').removeClass('active').next('li.selected').addClass('active');
                self._showItemDetails();
            });

            // List Limit
            $(dialog.limit + '-select').val($.Cookie.get('jce_' + this.options.plugin + '_limit') || this.options.listlimit);

            $(dialog.limit + '-select').change( function() {
                self._limitcount = 0;

                $.Cookie.set('jce_' + self.options.plugin + '_limit', $(this).val());
                self.refresh();
            });

            // Info navigation buttons
            $(dialog.limit + '-left', dialog.limit + '-left-end', dialog.limit + '-right', dialog.limit + '-right-end').click( function() {
                var x = 0, count = self._limitcount, limit = parseInt(self._limit);
                var id = $(this).attr('id');

                if (/-left/.test(id)) {
                    x = count - limit;
                    if (/-end/.test(id)) {
                        x = 0;
                    }
                }

                if (/-right/.test(id)) {
                    x = count + limit;
                    if (/-end/.test(id)) {
                        x = count = self._limitend;
                    }
                }

                self._limitcount = x;
                self.refresh();
            });

            // Check All checkbox
            $('span.checkbox', $('#check-all')).click( function(e) {
                var el = e.target;

                if ($(el).hasClass('checked')) {
                    $(el).removeClass('checked').attr('aria-checked', false);

                    $('span.checkbox', $(dialog.list)).removeClass('checked').attr('aria-checked', false);
                    // remove all selections
                    self._deselectItems();
                } else {
                    $(el).addClass('checked').attr('aria-checked', true);
                    
                    self._selectItems($('li.folder, li.file', $(dialog.list)), true);
                }
            });

            // Sortables

            $(dialog.sortExt).listsort({
                type 		: 'ext',
                lists		: ['#item-list'],
                selector 	: 'li.file'
            });

            $(dialog.sortName).listsort({
                type 		: 'name',
                lists		: ['#item-list'],
                selector 	: 'li.folder, li.file'
            });

            $('#sort-size').listsort({
                type 		: 'size',
                lists		: ['#item-list'],
                selector 	: 'li.file'
            });

            $('#sort-date').listsort({
                type 		: 'date',
                lists		: ['#item-list'],
                selector 	: 'li.folder, li.file'
            });

            // Show / hide search icon
            if (!this.options.search) {
                $('#block-search-icon').addClass('hide');
            }

            // show / hide details icon
            if (!this.options.details || !this._treeLoaded()) {
                $('#block-details-icon').addClass('hide');              
                $('#show-details span').addClass('disabled');
            }

            // Show / hide tree
            this._toggleTree(this._treeLoaded());

            $('#show-search').click( function() {
                $('#searchbox').toggleClass('hide').attr('aria-hidden', function() {
                	return $(this).hasClass('hide');
                });

                $(this).toggleClass('active');

                if ($(this).hasClass('active')) {
                    $('#search').focus();
                }
            });

            $('body').click( function(e) {
                if ($(e.target).is('#show-search, span.layout-icon.search') || $(e.target).parents('#searchbox').length) {
                    return;
                }

                $('#searchbox').addClass('hide').attr('aria-hidden', true);
                $('#show-search').removeClass('active');
            });

            // Searchables
            $(dialog.search).searchables({
                list 	: dialog.list,
                items	: '#item-list li.file',
                clear 	: $('span.search-icon', $('#searchbox')),
                onFind 	: function(e, items) {
                    if (items.length) {
                        self._deselectItems();
                        self._selectItems(items, true);
                    } else {
                        self._deselectItems();
                    }
                }

            });

            // Setup refresh button
            $(dialog.refresh).click( function() {
                self.refresh();
            });

            // Details button
            $('#show-details').click( function(e) {
                var span = e.target;               
                $(span).toggleClass('tree');

                self._showListDetails();
            });

            this._setupDir($(this.element).val());

            this._trigger('onInit');
        },
        
        _translate : function(s, ds) {
        	return $.Plugin.translate(s, ds);
        },

        /**
         * Set up the base directory
         * @param {String} src The base url
         */
        _setupDir : function(src) {
            var p = '/', f = '', base = this.options.dir, n = base.length;

            if (src) {
                if (src.substring(0, n) == base) {
                    p = $.String.dirname(src).replace(base, '', 'g') || '/';
                    f = $.String.basename(src) || '';
                }
            } else {
                p = $.Cookie.get('jce_' + this.options.plugin + '_dir') || '/';
                f = '';
            }

            this._dir = $.String.path('/', $.String.encodeURI(p));

            this._addReturnedItem({
                name : f
            });

            if (this._treeLoaded()) {
                // Initialize tree view
                this._createTree();
            } else {
                // Load folder / file list
                this._getList();
            }
        },

        _toggleTree : function(s) {        	
        	$('div.layout-left', '#browser').attr('aria-hidden', !s).toggle(s);
        	
        	$('#sort-size, #sort-date').attr('aria-hidden', s).toggle(!s);
        	
        	$('#browser').toggleClass('full-width', !s); 
        	
        	$('span.size, span.date', '#item-list').toggleClass('hide').attr('aria-hidden', s);
        },

        _buildList : function(o) {
            var self = this, h = '';
            
            // empty list
            $('#item-list').empty();

            if (!this._isRoot()) {
                h += '<li class="folder folder-up" title="Up"><a href="javascript:;">...</a></li>';
            }

            if (o.folders.length) {

                $.each(o.folders, function(i, e) {
                    var data = [];
                    $.each(e.properties, function(k, v) {
                        data.push('data-' + k + '="' + v + '"');
                    });

                    h += '<li class="folder '+ e.classes +'" id="'+ e.id +'" title="'+ e.name +'"' +
                    data.join(' ') +
                    '><span class="checkbox" role="checkbox" aria-checked="false"></span><a href="javascript:;" tabindex="-1">'+ e.name +'</a><span class="date" aria-hidden="true">' + $.String.formatDate(e.properties.modified) + '</span></li>';
                });
            }

            if (o.total.files) {
                $.each(o.files, function(i, e) {
                    var data = [];
                    $.each(e.properties, function(k, v) {
                        data.push('data-' + k + '="' + v + '"');
                    });

                    h += '<li class="file '+ $.String.getExt(e.name) + ' ' + e.classes +'" id="'+ e.id +'" title="'+ e.name +'"' +
                    data.join(' ') +
                    '><span class="checkbox" role="checkbox" aria-checked="false"></span><a href="javascript:;" tabindex="-1">'+ e.name +'</a><span class="date" aria-hidden="true">' + $.String.formatDate(e.properties.modified) + '</span><span class="size" aria-hidden="true">'+ $.String.formatSize(e.properties.size) +'</span></li>';
                });
            } else {
                h += '<li class="nofile">' + self._translate('no_files', 'No files') + '</li>';
            }
            
            $('#item-list').html(h);

            this._showListDetails();
        },

        _showListDetails : function() {
            var s = !$('span.layout-icon', '#show-details').hasClass('tree') && this._treeLoaded();

            this._toggleTree(s);
        },

        /**
         * Check if the Tree option is set and the Tree Class is loaded
         * return Boolean.
         */
        _treeLoaded : function() {
            return this.options.tree && typeof $.ui.tree != 'undefined';
        },

        /**
         * Initialize the Tree
         */
        _createTree : function() {
            var self = this, dialog = this.options.dialog;

            /* Initialise tree */
            this.setStatus(self._translate('message_tree', 'Building tree list...'), true);

            $(dialog.tree).tree({
                onInit : function(e, callback) {
                    $.JSON.request('getTree', self._dir, function(o) {
                        // Set default tree
                        $(dialog.tree).html(o);

                        if ($.isFunction(callback)) {
                            callback.apply();
                        }

                        // Load folder / file list
                        self._getList(self._dir);
                    });

                },

                // When a node is clicked
                onNodeClick : function(e, node) {
                    self._changeDir($(node).attr('id'));
                },

                // When a node is toggled and loaded
                onNodeLoad : function(e, node) {
                    var self = this;

                    $(this).tree('toggleLoader', node);

                    $.JSON.request('getTreeItem', $(node).attr('id'), function(o) {
                        if (o) {
                            if (!o.error) {
                                var ul = $('ul:first', node);

                                if (ul) {
                                    $(ul).remove();
                                }

                                $(self).tree('createNode', o.folders, node);
                                $(self).tree('toggleNodeState', node, true);
                            } else {
                                alert(o.error);
                            }
                        }
                        $(self).tree('toggleLoader', node);
                    }, this);

                }

            });
        },

        /**
         * Reset the Manager
         */
        _reset : function() {
            // Clear selects
            this._deselectItems();
            // Clear returns
            this._returnedItems	= [];

            // Close any dialogs
            $.each(this._dialog, function(i, n) {
                $(n).dialog('close');
            });

            // uncheck all checkboxes
            $('span.checkbox', $('#check-all')).removeClass('checked');
        },

        /**
         * Clear the Paste action
         */
        _clearPaste : function() {
            // Clear paste
            this._pasteaction = '';
            this._pastefiles  = '';

            this._hideButtons($('div.paste', '#buttons'));
        },

        /**
         * Set a status message
         * @param {String} message
         * @param {String} loading
         */
        setStatus : function(message, loading) {
            var cls = loading ? 'load' : '';
            $(this.options.dialog.status).attr('class', cls);

            $(this.options.dialog.status).html('<span>' + message + '</span>');
        },

        /**
         * Set a message
         * @param {String} message
         * @param {String} classname
         */
        _setMessage : function(message, classname) {
            return true;
        },

        /**
         * Sets a loading message
         */
        _setLoader : function() {
            this.setStatus(this._translate('message_load', 'Loading...'), true);
        },

        /**
         * Reset the message display
         */
        _resetMessage : function() {
            return true;
        },

        /**
         * Reset the status display
         */
        _resetStatus : function() {
            var dir = decodeURIComponent(this._dir);
            dir = dir.replace(/\//g, ' &rsaquo; ');
            this.setStatus(dir + ' ( ' + this._foldercount + ' ' + this._translate('folders', 'folders') + ', ' + this._filecount + ' ' + this._translate('files', 'files') + ' )');
        },

        /**
         * Get the parent directory
         * @return {String} s The parent/previous directory.
         */
        _getPreviousDir : function() {
            if (this._dir.length < 2) {
                return this._dir;
            }
            var dirs = this._dir.split('/');
            var s = '/';

            for (var i = 0; i < dirs.length-1; i++) {
                s = $.String.path(s, dirs[i]);
            }

            return s;
        },

        /**
         * Add an item to the returnedItems array
         * @return {Object} file The item.
         */
        _addReturnedItem : function(file) {
            this._returnedItems.push(file);
        },

        /**
         * Setup the returned file after upload
         * @param {String} file The returning file name.
         */
        _returnFile : function(file) {
            this._addReturnedItem({
                name: $.String.basename(file)
            });

            this._changeDir($.String.dirname(file));
        },

        /**
         * Set the current directory
         * @param {String} dir
         */
        _setDir: function(dir) {
            this._dir = dir;
        },
        
        /**
         * Get the base directory
         */
        getBaseDir : function() {
        	return this.options.dir;
        },

        /**
         * Get the current directory
         */
        getCurrentDir: function() {
            return this._dir;
        },

        /**
         Determine whether current directory is root
         */
        _isRoot : function() {
            return this._dir == '' || this._dir == '/';
        },

        /**
         * Change Directory
         * @param {String} dir
         */
        _changeDir: function(dir) {
            this._reset();
            this._limitcount = 0;
            this._setDir(dir);
            this._getList();
        },

        /**
         * Retrieve a list of files and folders
         */
        _getList : function() {
            this.action = $('form').attr('action') + '&upload-dir=' + this._dir;
            $.Cookie.set("jce_" + this.options.plugin + '_dir', this._dir);

            this._setLoader();

            this._hideButtons($('div.button', '#buttons'));

            this._limit = $(this.options.dialog.limit + '-select').val() || this.options.listlimit;

            $.JSON.request('getItems', [this._dir, this._limit, this._limitcount, this._vars], this._loadList, this);
        },

        /**
         * Refresh the file list
         */
        refresh : function() {
            this._reset();
            this._getList();
        },

        /**
         * Load the file/folder list into the container div
         * @param {Object} The folder/file JSON object
         */
        _loadList: function(o) {
            var dialog = this.options.dialog;

            //$('#folder-list').unbind('click').empty();
            
            // add unselectable
            $(dialog.list).attr('unselectable', 'on');

            this._foldercount 	= o.total.folders;
            this._filecount 	= o.total.files;

            this._limitend		= (o.total.folders + o.total.files) - this._limit;
            var count			= this._limitcount + o.folders.length + o.files.length;

            if (count < (o.total.folders + o.total.files)) {
                $(dialog.limit + '-right', dialog.limit + '-right-end').css('visibility', 'visible').attr('aria-hidden', false);
            } else {
                $(dialog.limit + '-right', dialog.limit + '-right-end').css('visibility', 'hidden').attr('aria-hidden', true);
            }

            if ((count - this._limit) > 0) {
                $(dialog.limit + '-left', dialog.limit + '-left-end').css('visibility', 'visible').attr('aria-hidden', false);
            } else {
                $(dialog.limit + '-left', dialog.limit + '-left-end').css('visibility', 'hidden').attr('aria-hidden', true);
            }

            if (o.folders.length) {
                this._dir = $.String.encodeURI($.String.dirname(o.folders[0].id) || '/');
            } else if (o.files.length) {
                this._dir = $.String.encodeURI($.String.dirname(o.files[0].id) || '/');
            }

            // Add folder-up button
            if (!this._isRoot()) {
                $('#folder-list').append('<li class="folder-up" title="Up"><a href="javascript:;>...</a></li>');
            }

            if (this._treeLoaded()) {
                $(dialog.tree).tree('createNode', o.folders, this._dir);
            }

            // Alternate loadList function
            this._trigger('onBeforeBuildList', null, o);

            // Build the file / folder list
            this._buildList(o);

            // Alternate loadList function
            this._trigger('onAfterBuildList', null, o);

            if (this._returnedItems.length) {
                this._selectItemsByName(this._returnedItems);
                this._returnedItems = [];
            }

            if (this._pastefiles) {
                this._showPasteButton();
            }

            this._resetStatus();
            this._resetMessage();
            this._trigger('onListComplete');
        },

        /**
         * Execute a command
         * @param {String} The command name
         * @param {String} The command type
         */
        _execute : function(name) {
            var self 	= this;
            var dir 	= this._dir;
            var list	= this._serializeSelectedItems();

            var site 	= $.Plugin.getURI(true);
            var base	= this.options.dir;

            switch(name) {
                case 'help':
                    $.Plugin.help();
                    break;
                case 'insert':
                    this._trigger('onFileInsert', null, $('li.selected', '#item-list').get(0));
                    break;
                case 'view':
                    var $item		= $('li.selected.active:first', '#item-list');
                	var id			= $item.attr('id');
                    var url 		= /http(s)?:\/\//.test(id) ? id : $.String.path($.String.path(site, base), id);
                    var name 		= $.String.basename($item.attr('title'));

                    var viewable 	= 'jpeg|jpg|gif|png|avi|wmv|wm|asf|asx|wmx|wvx|mov|qt|mpg|mp3|mp4|m4v|mpeg|ogg|ogv|webm|swf|flv|f4v|xml|dcr|rm|ra|ram|divx|html|htm|txt|rtf|pdf|doc|docx|xmls|ppt';

                    if (new RegExp('\.(' + viewable + ')$', 'i').test(name)) {
                        if(/\.(jpeg|jpg|gif|png|avi|wmv|wm|asf|asx|wmx|wvx|mov|qt|mpg|mp3|mp4|m4v|mpeg|ogg|ogv|webm|swf|flv|f4v|xml|dcr|rm|ra|ram|divx)/i.test(name)) {
                            $.Dialog.media(name, url);
                        } else {
                            $.Dialog.iframe(name, url, {
                                onFrameLoad : function(e) {
                                    var iframe 	= $('div.iframe-preview iframe').get(0);
                                    var h 		= iframe.contentWindow.document.body.innerHTML;
                                    var tmpDiv 	= document.createElement('div');

                                    $(tmpDiv).html(h);

                                    function toRelative(s) {
                                        s = $.URI.toRelative(s);
                                        return s.replace(/^administrator\//, '');
                                    }

                                    $('img, embed', $(tmpDiv)).each( function() {
                                        var s = toRelative($(this).attr('src'));

                                        if (!/http(s)?:\/\//.test(s)) {
                                            s = $.String.path(site, s);
                                        }
                                        $(this).attr('src', s);
                                    });

                                    $('a, area', $(tmpDiv)).each( function() {
                                        var s = toRelative($(this).attr('href'));

                                        if (!/http(s)?:\/\//.test(s)) {
                                            s = $.String.path(site, s);
                                        }
                                        $(this).attr('href', s);
                                    });

                                    $('object', $(tmpDiv)).each( function() {
                                        $('param[name=movie], param[name=src]', this).each( function() {
                                            var s = toRelative($(this).attr('value'));
                                            if (!/http(s)?:\/\//.test(s)) {
                                                s = string.path(site, s);
                                            }
                                            $(this).attr('value', s);
                                        });

                                    });
                                    iframe.contentWindow.document.body.innerHTML = tmpDiv.innerHTML;
                                }

                            });
                        }
                    }
                    break;
                case 'upload':
                    this._dialog['upload'] = $.Dialog.upload({
                        extended 	: self.options.upload,
                        onOpen 		: function() {                   	                    	
                            // Set hidden dir value to current dir
                            $('#upload-dir').val(self._dir);

                            // Set overwrite options
                            var o = {
                                'overwrite' : '<option value="0">' + self._translate('overwrite', 'Overwrite file') + '</option>',
                                'unique'	: '<option value="1">' + self._translate('unique', 'Create unique name') + '</option>'
                            };

                            $.each(self.options.upload.conflict, function(k, v) {
                            	$('#upload-overwrite').append(o[v]);
                            });

                            /**
                             * Private internal function
                             * Check file name against list
                             * @param {Object} name File name
                             */
                            function _checkName(file) {
                                var found, msg = self._translate('file_exists_alert', 'A file with the same name exists in the target folder.');
                                var name = $.String.safe(file.name);

                                $('li', 'file-list').each( function() {
                                    if (name == $(this).attr('title')) {
                                        found = true;
                                    }
                                });

                                var el = file.element, span = $('span.queue-name:first', el);

                                if (found) {
                                    if (!$(el).hasClass('exists')) {
                                        $(el).addClass('exists');
                                        $(span).attr('title', name + '::' + msg);

                                        $(span).tips();
                                    }
                                } else {
                                    if ($(el).hasClass('exists')) {
                                        $(el).removeClass('exists');

                                        $(span).attr('title', name);
                                    }
                                }

                                return true;
                            }

                            // Initialize uploader
                            $('#upload-queue').uploader($.extend({
                            	swf 	: 	site + 'components/com_jce/editor/libraries/plupload/plupload.flash.swf',
                            	xap		: 	site + 'components/com_jce/editor/libraries/plupload/plupload.silverlight.xap',
                                url		:	$('form:first').attr('action'),
                                field	:	$('input[name=file]:first'),
                                fileSelect 	: function(e, file) {
                                    return _checkName(file);
                                },

                                fileRename : function(e, file) {
                                    return _checkName(file);
                                },

                                fileComplete : function(e, file) {
                                    if ($.type(file) == 'string') {
                                        file = {name : file};
                                    }

                                    self._addReturnedItem(file);
                                },

                                uploadComplete : function(up, files) {
                                	$('#upload-submit').disabled = false;
                                	
                                	if (up.total == files.length && $('#upload-queue').uploader('getErrors') == 0) {
                                         // Reset action
                                         self.action = $('form:first').action;
                                         // Refresh file list
                                         self._getList();
                                    	
                                    	window.setTimeout(function(){
                                    		$(self._dialog['upload']).dialog('close');
                                    	}, 1000);
                                    }
                                }

                            }, self.options.upload));
                        },

                        dragStop : function() {
                            $('#upload-queue').uploader('refresh');
                        },

                        upload : function() {
                            if ($('#upload-queue').uploader('isUploading'))
                                return false;
                            
                            var data = {'action' : 'upload', 'format' : 'json'}, fields = $.merge($(':input', 'form').serializeArray(), $(':input', '#upload-body').serializeArray());

                            $.each(fields, function(i, field) {
                                data[field.name] = field.value;
                            });

                            $('#upload-queue').uploader('upload', data);

                            return false;
                        },

                        close : function() {
                            $('#upload-queue').uploader('close');
                        }

                    });
                    break;
                case 'folder_new':
                    this._dialog['folder_new'] = $.Dialog.prompt(self._translate('folder_new', 'New Folder'), {
                        text	: self._translate('name', 'Name'),
                        confirm	: function(v) {
                            if (v) {
                                self._setLoader();
                                $.JSON.request('folderNew', [dir, $.String.safe(v)], function(o) {
                                    if (!o.error.length) {
                                        self._trigger('onFolderNew');
                                        self.refresh();
                                        $(self._dialog['folder_new']).dialog('close');
                                    } else {
                                        self._raiseError(o.error);
                                    }
                                });

                            }
                        }

                    });
                    break;

                // Cut / Copy operation
                case 'copy':
                case 'cut':
                    this._pasteaction 	= name;
                    this._pastefiles 	= list;

                    this._showPasteButton();

                    break;

                // Paste the file
                case 'paste':
                    var fn = (this._pasteaction == 'copy') ? 'copyItem' : 'moveItem';
                    this._setLoader();

                    $.JSON.request(fn, [this._pastefiles, dir], function(o) {
                        if (!o.error.length) {
                            self._trigger('onPaste');
                            self._clearPaste();
                            self.refresh();
                        } else {
                            self._raiseError(o.error);
                        }
                    });

                    break;

                // Delete a file or folder
                case 'delete':
                    var msg = self._translate('delete_item_alert', 'Delete Selected Item(s)');

                    this._dialog['confirm'] = $.Dialog.confirm(msg, function(state) {
                        if (state) {
                            self._setLoader();
                            $.JSON.request('deleteItem', list, function(o) {
                                if (!o.error.length) {

                                    if (o.folders.length) {
                                        self._trigger('onFolderDelete', null, o.folders);
                                    }

                                    if (o.files.length) {
                                        self._trigger('onFileDelete', null, o.files);
                                    }

                                    self.refresh();
                                } else {
                                    self._raiseError(o.error);
                                }
                            });

                        }
                    }

                    );
                    break;

                // Rename a file or folder
                case 'rename':
                    var s = this.getSelectedItems(0);
                    var v = $.String.basename(list);

                    if ($(s).hasClass('file')) {
                        v = $.String.basename($.String.stripExt(list));
                    }

                    this._dialog['rename'] = $.Dialog.prompt(self._translate('rename', 'Rename Item'), {
                        text	: self._translate('name', 'Name'),
                        value 	: v,
                        confirm : function(v) {
                            var name = $.String.safe(v);
                            self._dialog['confirm'] = $.Dialog.confirm(self._translate('rename_item_alert', 'Renaming files/folders will break existing links. Continue?'), function(state) {
                                if (state) {
                                    self._setLoader();
                                    $.JSON.request('renameItem', [list, name], function(o) {
                                        if (!o.error.length) {
                                            self._reset();
                                            var item = $.String.path(self._dir, name);

                                            // folder rename successful
                                            if (o.folders.length) {
                                                self._trigger('onFolderRename', null, list, item);
                                            }

                                            // file rename successful
                                            if (o.files.length) {
                                                self._trigger('onFileDelete', null, item);
                                            }

                                            if (item) {
                                                self._addReturnedItem({
                                                    name: item
                                                });
                                            }

                                            $(self._dialog['rename']).dialog('close');
                                            self._getList();
                                        } else {
                                            self._raiseError(o.error);
                                        }
                                    });

                                }
                            });

                        }

                    });
                    break;
            }
        },

        /**
         * Show an error dialog
         * @param {String} error
         */
        _raiseError : function(error) {
            var self = this, err = '';

            if ($.type(error) == 'array') {
                err += '<ul class="error-list">';
                $.each(error, function(i, e) {
                    err += '<li>' + e + '</li>';
                });

                err += '</ul>';
            } else {
                err = error;
            }

            this._dialog['alert'] = $.Dialog.alert(err, {
                close : function() {
                    self.refresh();
                }

            });
        },

        /**
         * Add an array of actions
         * @param {Object} actions
         */
        _addActions : function(actions) {
            var self = this;

            $.each(actions, function(i, action) {
                self._addAction(action);
            });

        },

        /**
         * Add an action to the Manager
         * @param {Object} options
         */
        _addAction : function(o) {
            var self = this;
            var name 	= o.name || '';

            var fn		= eval(o.action) || this._execute;
            var action 	= document.createElement('span');

            $(action).addClass('action');

            if (name) {
                $(action).attr({'id': name, 'title': o.title, 'role' : 'button', 'labelledby' : name + '_label'}).addClass(name).append('<span id="' + name + '_label" aria-hidden="true">' + o.title + '</span>');

                if (o.icon) {
                    $(action).css('background-image', $.String.path($.Plugin.getPath(this.options.plugin), o.icon));
                }

                if (o.name) {
                    $(action).click( function() {
                        fn.call(self, name);
                    });
                }

                this._actions[name] = action;
            }

            $(this.options.dialog.actions).append('<span class="spacer"></span>').append(action);

            if (!$.support.cssFloat && !window.XMLHttpRequest) {
                $(action).hover(function() {
                    $(this).addClass('hover');
                }, function() {
                    $(this).removeClass('hover');
                });
            }
        },

        /**
         * Get an action by name
         * @param {String} name
         */
        _getAction : function(name) {
            return this._actions[name];
        },

        /**
         * Add an array of buttons to the Manager
         * @param {Object} buttons
         */
        _addButtons : function(buttons) {
            var self = this;

            $.each(buttons.folder, function(i, button) {
                if (button) {
                	self._addButton(button, 'folder');
                }
            });

            $.each(buttons.file, function(i, button) {
            	if (button) {
            		self._addButton(button, 'file');
            	}
            });

        },

        /**
         * Add a button to the Manager
         * @param {Object} o Button Object
         * @param {String} type
         */
        _addButton : function(o, type) {
            var self = this, fn = eval(o.action) || this._execute;
            var dialog = this.options.dialog;

            // only create button type once
            var button = $('div.' + o.name, $(dialog.buttons));

            if (!button.length) {
                button = document.createElement('div');

                $(button).attr({'title' : o.title, 'role' : 'button'}).append('<span id="' + name + '_label" aria-hidden="true">' + o.title + '</span>');

                if (o.icon) {
                    $(button).css('background-image', $.String.path($.Plugin.getPath(this.options.plugin), o.icon));
                }

                if (o.name) {
                    var n = o.name;

                    $(button).click( function() {
                        if ($('li.selected', '#item-list').length) {
                            fn.call(self, n, type);
                        }
                    });
                }

                if (!$.support.cssFloat && !window.XMLHttpRequest) {
                    $(button).hover( function() {
                        $(this).addClass('hover');
                    }, function() {
                        $(this).removeClass('hover');
                    }

                    );
                }

                $(dialog.buttons).append(button);
                $(button).addClass('button ' + o.name + ' hide');
            }

            this._buttons[type][o.name] = {
                'name'		: o.name,
                'element'	: button,
                'trigger'	: o.trigger,
                'multiple'	: o.multiple
            };
        },

        /**
         * Hide all buttons
         */
        _hideAllButtons : function() {
            var self = this;

            $('div.button').each( function() {
                self._hideButton(this);
            });

        },

        /**
         * Hide buttons by type
         * @param {String} type The button type
         */
        _hideButtons : function(buttons) {
            var self = this;

            $.each(buttons, function(i, button) {
                self._hideButton(button);
            });

        },

        /**
         * Hide a button
         * @param {String} button The button to hide
         */
        _hideButton : function(button) {
            $(button).removeClass('show').addClass('hide').attr('aria-hidden', true);
        },

        /**
         * Show all buttons
         * @param {String} type The button type to show
         */
        _showButtons : function() {
            var self = this;

            this._hideAllButtons();

            var folder 	= $('li.folder.selected', '#item-list');
            var file	= $('li.file.selected', '#item-list');

            // multiple type selection
            if (file.length && folder.length) {
                var buttons = {};

                var filebtns 	= this._buttons['file'];
                var folderbtns 	= this._buttons['folder'];

                $.each(filebtns, function(k, o) {
                    if (!o.trigger && o.multiple) {
                        if (folderbtns[k]) {
                            buttons[k] = o;
                        }
                    }
                });

                $.each(folderbtns, function(k, o) {
                    if (!o.trigger && o.multiple) {
                        if (filebtns[k]) {
                            buttons[k] = o;
                        }
                    }
                });

                $.each(buttons, function(k, o) {
                    self._showButton(o.element, true);
                });

            } else {
                // set folder as default type
                var type = file.length ? 'file' : 'folder';

                $.each(this._buttons[type], function(k, o) {
                    if (!o.trigger) {
                        self._showButton(o.element, o.multiple);
                    }
                });

            }
        },

        /**
         * Show a button
         * @param {String} button The button to show
         * @param {Boolean} multiple Whether a button is a multiple selection action
         */
        _showButton : function(button, multiple) {
            if (button) {
                if ($(button).hasClass('hide')) {
                    $(button).removeClass('hide').addClass('show').attr('aria-hidden', false);
                }

                if ($('li.selected', '#item-list').length > 1 && !multiple) {
                    this._hideButton(button);
                }
            }
        },

        /**
         * Get a button
         * @param {String} type The button type
         * @param {String} name The button name
         */
        _getButton : function(type, name) {
            return this._buttons[type][name] || null;
        },

        /**
         * Show the paste button
         */
        _showPasteButton : function() {
            this._showButton($('div.paste', '#buttons'), true);
        },

        /**
         * Determine whether an item is selected
         * @param {Object} el The list item
         */
        _isSelectedItem : function(el) {
            return $(el).is('li.selected');
        },

        /**
         * Deselect all list items
         */
        _deselectItems : function() {
            var dialog = this.options.dialog;

            // deselect item and uncheck checkboxes
            $('li.selected', '#item-list').removeClass('selected active').children('span.checkbox').removeClass('checked').attr('aria-checked', false);

            $(dialog.info).empty();
            $(dialog.comments).empty();

            // Shortcut for nav
            var nav = dialog.nav;

            $.each([nav + '-left', nav + '-right', nav + '-text'], function(i, el) {
                $(el).css('visibility', 'hidden').attr('aria-hidden', true);
            });

            this._hideAllButtons();

            $('span.checkbox', '#check-all').removeClass('checked');
        },

        /**
         * Select an array of items
         * @param {Array} items The array of items to select
         * @param {Boolean} show Show item properties
         */
        _selectItems : function(items, show) {
            $(items).addClass('selected').children('span.checkbox').addClass('checked').attr('aria-checked', true);

            if (show) {
                this._showSelectedItems();
            }

            if ($('span.checked', '#item-list').length == $('li.selected', '#item-list').length) {
                $('span.checkbox', '#check-all').addClass('checked').attr('aria-checked', true);
            }
        },

        /**
         * Remove items from a selection
         * @param {Array} el Array of elements to remove
         * @param {Boolean} show Show remaining item properties
         */
        _removeSelectedItems : function(items, show) {
            $(items).removeClass('selected').children('span.checkbox').removeClass('checked').attr('aria-checked', false);

            if (show) {
                this._showSelectedItems();
            }
        },

        /**
         * Return selected items by key or all selected items
         * @param {String} key Item key
         */
        getSelectedItems : function(key) {
            var $items = $('li.selected', '#item-list');
            
            return $items.get(key) || $items;
        },

        /**
         * Process a selection click
         * @param {String} e The click event.
         * @param {Boolean} multiple Allow multiple selections.
         */
        _setSelectedItems : function(e, multiple) {
            var checkbox = false;

            // the selected element
            var el = e.target;
            
            if (e.type == 'keydown') {
            	// element is probably parent ul, so get last selected item
            	el = $('li.selected:last', '#item-list').get(0);
            	
            	var $list = $(this.options.dialog.list);
            	
            	// change target for keydown
                if (e.which == 38) {
                	el = el.previousSibling;
                }
                
                if (e.which == 40) {
                	el = el.nextSibling;
                }
                
                if (!el) {
                	return;
                }

                if (el.offsetTop > $list.innerHeight() || el.offsetTop < $list.scrollTop()) {
                	$list.scrollTop((el.offsetTop + $(el).innerHeight()) - $list.height());
                }
            }

            if ($(el).hasClass('checkbox')) {
                multiple = true;
                checkbox = true;
            }

            // If not li element, must be a so get parent li
            if (el.nodeName != 'LI')
                el = el.parentNode;
            
            var selected = $('li.selected', '#item-list');

            // Prevent double clicking
            if (this._isSelectedItem(el) && selected.length == 1) {
                e.preventDefault();
                return false;
            }

            // Get items
            var items = $('li.folder, li.file', '#item-list');
            // get key
            var ctrl = (e.ctrlKey || e.metaKey), shift = e.shiftKey;

            // Single click
            if (!ctrl && !shift && !checkbox || !multiple) {
                // uncheck all boxes
                $('span.checkbox', el).removeClass('checked').attr('aria-checked', false);

                // deselect all
                this._deselectItems();
                // select item
                this._selectItems([el], true);

                // ctrl & shift
            } else if (multiple && (ctrl || shift || checkbox)) {
                // ctrl
                if (ctrl || checkbox) {
                    if (this._isSelectedItem(el)) {
                        this._removeSelectedItems([el], true);
                    } else {
                        this._selectItems([el], true);
                    }
                }

                // shift
                if (shift) {
                    if (selected.length) {
                        // selected item index
                        var si 			= $(el, selected).index();
                        // click item index
                        var ci			= $(el, items).index();
                        var selection 	= [];

                        // Clear selection
                        this._deselectItems();

                        // Clicked item further up list than selected item
                        if (ci > si) {
                            for (var i=ci; i>=si; i--) {
                                selection.push(items[i]);
                            }
                        } else {
                            // Clicked item further down list than selected item
                            for (var i=si; i>=ci; i--) {
                                selection.push(items[i]);
                            }
                        }
                        this._selectItems(selection, true);
                    } else {
                        this._selectItems([el], true);
                    }
                }
            }
        },

        /**
         * Show the selected items' details
         */
        _showSelectedItems : function() {
            var $items = $('li.selected', '#item-list'), n = $items.length;

            // reset if no selection
            if (!n) {
                this._reset();
            } else {
                // make the first item active
            	$items.first().addClass('active');
            	
            	this._showItemDetails();
            }
        },

        /**
         * Select an item (file) by name
         * @param {String} name The file name.
         */
        _selectItemsByName : function(files, type) {
            var self = this, items = [];
            type = type || 'file';

            if ($.type(files) == 'string') {
                files = [files];
            }
            
            var insert = false;

            $.each(files, function(i, file) {
                if (file && file.name) {
                    file.name = $.String.basename(file.name);
                    
                    var item = $('li.' + type + '[title="'+ decodeURIComponent(file.name) +'"]', '#item-list');

                    if (file.insert) {
                        insert = true;
                        items = item;
                    	self._trigger('onFileClick', null, $(item).get(0));
                    }
                    
                    if (!insert) {
                    	$.merge(items, item);
                    }
                }
            });

            if (items.length) {
            	var pos = $(items[0]).position();
            	$(this.options.dialog.list).animate({scrollTop : pos.top}, 1500);
            }

            // Select items and display properties
            this._selectItems(items, true);
        },

        /**
         * Serialize the current item selection, add current dir to path
         */
        _serializeSelectedItems : function() {
            var self = this;
            
        	return $('li.selected', '#item-list').map(function() {
                return $.String.path(self._dir, $(this).attr('title'));
            }).get().join(',');
        },

        /**
         * Show a file /folder properties / details
         */
        _showItemDetails : function() {
            var nav = this.options.dialog.nav, $items = $('li.selected', '#item-list'), n = $items.length;
            
            // show navigation buttons
            if (n > 1) {
            	// get active item index
                var active 	= $items.filter('.active').get(0);
                var index	= $items.toArray().indexOf(active);

                if (index) {
                    $(nav + '-left').css('visibility', 'visible').attr('aria-hidden', false);
                } else {
                    $(nav + '-left').css('visibility', 'hidden').attr('aria-hidden', true);
                }

                if (index + 1 < n) {
                    $(nav + '-right').css('visibility', 'visible').attr('aria-hidden', false);
                } else {
                    $(nav + '-right').css('visibility', 'hidden').attr('aria-hidden', true);
                }

                $(nav + '-text').css('visibility', 'visible').html(index + 1 + ' of ' + n);
            // hide navigation buttons
            } else {
            	$([nav + '-left', nav + '-right', nav + '-text']).css('visibility', 'hidden').attr('aria-hidden', true);
            }

            // show relevant buttons
            this._showButtons();
            // get item details
            this._getItemDetails();
        },

        /**
         * Get a file or folder's properties
         */
        _getItemDetails : function() {
            var self = this, dialog = this.options.dialog;
            var item = $('li.selected.active', '#item-list'), title = $.String.basename($(item).attr('title'));
            var type = $(item).hasClass('folder') ? 'folder' : 'file';

            $(dialog.info).empty().addClass('loader');

            // create item path
            var path = $.String.path(this._dir, $.String.encodeURI(title));
            // get item name
            var name = (type == 'file') ? $.String.stripExt(title) : title;
            
            // shorten name
            if (name.length > 25) {
                name = name.substr(0, 25) + '...';
            }
            
            // create properties list
            var info = document.createElement('dl');
            $(info).append('<dt>' + name + '</dt><dd>' + self._translate(type, $.String.ucfirst(type)) + '</dd><dd id="info-properties"><dl></dl></dd>');
            
            // additional data for file items
            if (type == 'file') {
            	$(info).append('<dd id="info-preview"></dd>');
            }
            
            // remove the loader and append info
            $(dialog.info).removeClass('loader').empty().append(info);

            var comments = '';

            // check if item writable - show warning
            if ($(item).hasClass('notwritable')) {
                comments +=
                '<dd class="comments ' + type + ' notwritable">' +
                '<span class="hastip" title="'+ self._translate('notwritable_desc', 'Unwritable') +'">' +self._translate('notwritable', 'Unwritable') +'</span>' +
                '</dd>';
            }
            
            // check if item websafe - show warning
            if ($(item).hasClass('notsafe')) {
                comments +=
                '<dd class="comments ' + type + ' notsafe">' +
                '<span class="hastip" title="'+ self._translate('bad_name_desc', 'Bad file or folder name') +'">'+ self._translate('bad_name', 'Bad file or folder name') +'</span>' +
                '</dd>';
            }

            // process triggered buttons
            if ($(item).data('trigger')) {
                $.each($(item).data('trigger'), function(i, v) {
                    if (v !== '') {
                        var button = self._getButton(type, v);
                        if (button) {
                            self._showButton(button.element, button.multiple);
                        }
                    }
                });           
            }

            // Properties
            $.each($(item).data(), function(k, v) {
                if (/^(preview|trigger|width|height)$/.test(k))
                    return;

                if (k == 'size') {
                    v = $.String.formatSize(v);
                }
                
                if (k == 'modified') {
                	v = $.String.formatDate(v);
                }

                $('#info-properties dl').append('<dd id="info-' + k.toLowerCase() + '">' + self._translate('' + k, k) + ': ' + v + '</dd>');
            });
            
            // Dimensions (will only apply to file items)
            if ($(item).data('width') && $(item).data('height')) {
                $('#info-properties dl').append('<dd id="info-dimensions">' + self._translate('dimensions', 'Dimensions') + ': ' + $(item).data('width') + ' x ' + $(item).data('height') + '</dd>');
            }

            // Preview (will only apply to file items)
            if ($(item).data('preview')) {
                $('#info-preview').empty().append(
                '<dl>' +
                '<dt>' + self._translate('preview', 'Preview') + ': </dt>' +
                '<dd style="height:80px;" class="loader"></dd>' +
                '</dl>'
                );

                var src = $(item).data('preview');

                var img = new Image();

                img.onload = function() {
                    var w = img.width;
                    var h = img.height;

                    // set dimensions
                    if ($(item).data('width') || $(item).data('height')) {
                        $('#info-dimensions').html(self._translate('dimensions', 'Dimensions') + ': ' + w + ' x ' + h);
                    }

                    if (w > 100) {
                        h = h * (100 / w);
                        w = 100;
                        if (h > 80) {
                            w = w * (80 / h);
                            h = 80;
                        }
                    } else if (h > 80) {
                        w = w * (80 / h);
                        h = 80;
                        if (w > 100) {
                            h = h * (100 / w);
                            w = 100;
                        }
                    }
                    $('dd', $('#info-preview')).removeClass('loader').html('<img src="'+ src +'" width="'+ Math.round(w) +'" height="'+ Math.round(h) +'" title="Preview" />');
                };

                img.onerror = function() {
                    $('dd', $('#info-preview')).removeClass('loader').addClass('preview-error');
                };
                
                img.src = $.String.encodeURI(src);
            }

            if (comments) {
                $(dialog.comments).empty().append('<dl><dt>' + self._translate('comments', 'Comments') + '</dt>' + comments + '</dl>');
            }

            $('span.hastip', $(dialog.comments)).tips();
            
            var cb = (type == 'folder') ? 'onFolderDetails' : 'onFileDetails';

            self._trigger(cb, null, item);
        },

        destroy: function() {
            $.Widget.prototype.destroy.apply( this, arguments );
        }

    });

    $.extend($.ui.MediaManager, {
        version: "2.0.0beta2"
    });
})(jQuery);